﻿#region Using Statements
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using starLiGHT;

#endregion

#region COPYRIGHT

/*
    Copyright (c) 2008, 2009, 2010 
        Roland Rosenkranz (Glatzemann@email.de)
*/

#endregion

#region LICENSE

/*
    This file is part of starLiGHT.Collision.

    starLiGHT.Collision is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    starLiGHT.Collision is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with starLiGHT.Collision.  If not, see <http://www.gnu.org/licenses/>.

    
    ADDITIONAL (commercial) LICENSES for starLiGHT.Collision are available on request.
*/

#endregion

#region Box2D Copyright
/*
* Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#endregion

#region Version Stuff
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 1486                  $:  Revision of last commit *
// * $Author:: glatzemann         $:  Author of last commit   *
// * $Date:: 2010-10-08 15:55:47 #$:  Date of last commit     *
// ************************************************************

// **************[ Box2D-Repository Info ]*********************
// Header-File: http://code.google.com/p/box2d/source/browse/trunk/Box2D/Box2D/Dynamics/b2Fixture.h
// Revision   : r142
// Change-Date: 2011-02-03
//
// Source-File: http://code.google.com/p/box2d/source/browse/trunk/Box2D/Box2D/Dynamics/b2Fixture.cpp
// Revision   : r142
// Change-Date: 2011-02-03
//
// Status     : Finished!
#endregion

namespace starLiGHT.Physic.TwoD
{

    /// <summary>This holds contact filtering data.</summary>
    public struct Filter
    {
        /// <summary>The collision category bits. Normally you would just set one bit.</summary>
        public ushort categoryBits;

        /// <summary>The collision mask bits. This states the categories that this
        /// shape would accept for collision.</summary>
        public ushort maskBits;

        /// <summary>Collision groups allow a certain group of objects to never collide (negative)
        /// or always collide (positive). Zero means no collision group. Non-zero group
        /// filtering always wins against the mask bits.</summary>
        public short groupIndex;

        public Filter(short groupIndex, ushort categoryBits, ushort maskBits)
        {
            this.categoryBits = categoryBits;
            this.maskBits = maskBits;
            this.groupIndex = groupIndex;
        }
    };

    /// <summary>A fixture definition is used to create a fixture. This class defines an
    /// abstract fixture definition. You can reuse fixture definitions safely.</summary>
    public class FixtureDef
    {
        /// <summary>The constructor sets the default fixture definition values.</summary>
        public FixtureDef()
        {
            shape = null;
            userData = null;
            friction = 0.2f;
            restitution = 0.0f;
            density = 0.0f;
            filter.categoryBits = 0x0001;
            filter.maskBits = 0xFFFF;
            filter.groupIndex = 0;
            isSensor = false;
        }

        /// <summary>The shape, this must be set. The shape will be cloned, so you
        /// can create the shape on the stack.</summary>
        public Shape shape;

        /// <summary>Use this to store application specific fixture data.</summary>
        public object userData;

        /// <summary>The friction coefficient, usually in the range [0,1].</summary>
        public float friction;

        /// <summary>The restitution (elasticity) usually in the range [0,1].</summary>
        public float restitution;

        /// <summary>The density, usually in kg/m^2.</summary>
        public float density;

        /// <summary>A sensor shape collects contact information but never generates a collision
        /// response.</summary>
        public bool isSensor;

        /// <summary>Contact filtering data.</summary>
        public Filter filter;
    }

    /// This proxy is used internally to connect fixtures to the broad-phase.
    public class FixtureProxy
    {
        public AABB aabb;
        public Fixture fixture;
        public int childIndex;
        public int proxyId;
    }

    /// <summary>A fixture is used to attach a shape to a body for collision detection. A fixture
    /// inherits its transform from its parent. Fixtures hold additional non-geometric data
    /// such as friction, collision filters, etc.
    /// Fixtures are created via Body::CreateFixture.</summary>
    /// <remarks>you cannot reuse fixtures.</remarks>
    public class Fixture
    {
        /// <summary>Get the type of the child shape. You can use this to down cast to the concrete shape.</summary>
	    /// <returns>the shape type.</returns>
        new public ShapeType GetType()
        {
            return m_shape.GetType();
        }

        /// <summary>
        /// Get the child shape. You can modify the child shape, however you should not change the
	    /// number of vertices because this will crash some collision caching mechanisms.
        /// Manipulating the shape may lead to non-physical behavior.
        /// </summary>
        public Shape GetShape()
        {
            return m_shape;
        }

        /// <summary>Set if this fixture is a sensor.
        /// You must call World::Refilter to update existing contacts.</summary>
        public void SetSensor(bool sensor)
        {
            m_isSensor = sensor;
        }

        /// <summary>Is this fixture a sensor (non-solid)?</summary>
	    /// <returns>true if the shape is a sensor.</returns>
        public bool IsSensor()
        {
            return m_isSensor;
        }

        /// <summary>
        /// Set the contact filtering data. This will not update contacts until the next time
        /// step when either parent body is active and awake.
        /// This automatically calls Refilter.
        /// </summary>
        public void SetFilterData(Filter filter)
        {
            m_filter = filter;

            Refilter();
        }

        /// <summary>Get the contact filtering data.</summary>
        public void GetFilterData(out Filter filter)
        {
            filter = m_filter;
        }

        /// <summary>
        /// Call this if you want to establish collision that was previously disabled by ContactFilter.ShouldCollide.
        /// </summary>
        public void Refilter()
        {
            if (m_body == null)
            {
                return;
            }

            // Flag associated contacts for filtering.
            ContactEdge edge = m_body.ContactList;
            while (edge != null)
            {
                Contact contact = edge.contact;
                Fixture fixtureA = contact.GetFixtureA();
                Fixture fixtureB = contact.GetFixtureB();
                if (fixtureA == this || fixtureB == this)
                {
                    contact.FlagForFiltering();
                }

                edge = edge.next;
            }

            World world = m_body.World;

            if (world == null)
            {
                return;
            }

            // touch each proxy so that new pairs may be created
            BroadPhase broadPhase = world.m_contactManager.m_broadPhase;
            for (int i = 0; i < m_proxyCount; ++i)
            {
                broadPhase.TouchProxy(m_proxies[i].proxyId);
            }
        }

        /// <summary>Get the parent body of this fixture. This is NULL if the fixture is not attached.</summary>
        /// <returns>the parent body.</returns>
        public Body GetBody()
        {
            return m_body;
        }

        /// <summary>Get the next fixture in the parent body's fixture list.</summary>
	    /// <returns>the next shape.</returns>
        public Fixture GetNext()
        {
            return m_next;
        }

        public void SetDensity(float density)
        {
            System.Diagnostics.Debug.Assert(Math.IsValid(density) && density >= 0.0f);
            m_density = density;
        }

        public float GetDensity()
        {
            return m_density;
        }

        /// <summary>Get the user data that was assigned in the fixture definition. Use this to
	    /// store your application specific data.</summary>
        public object GetUserData()
        {
            return m_userData;
        }

        /// <summary>Set the user data. Use this to store your application specific data.</summary>
        public void SetUserData(Object data)
        {
            m_userData = data;
        }

        /// <summary>Test a point for containment in this fixture. This only works for convex shapes.</summary>
	    /// <param name="p">a point in world coordinates.</param>
        public bool TestPoint(Vector2 p)
        {
            Transform xf = m_body.Transform;
            return m_shape.TestPoint(ref xf, p);
        }

        /// <summary>
        /// Cast a ray against this shape
        /// </summary>
        /// <param name="output">the ray-cast results</param>
        /// <param name="input">the ray-cast input parameters.</param>
        /// <returns></returns>
        public bool RayCast(out RayCastOutput output, ref RayCastInput input, int childIndex)
        {
            Transform xf = m_body.Transform;
            return m_shape.RayCast(out output, ref input, ref xf, childIndex);
        }

        public void GetMassData(out MassData massData)
        {
            m_shape.ComputeMass(out massData, m_density);
        }

        /// <summary>Get the coefficient of friction.</summary>
        public float GetFriction()
        {
            return m_friction;
        }

        /// <summary>
        /// Set the coefficient of friction. This will immediately update the mixed friction
        /// on all associated contacts.
        /// </summary>
        public void SetFriction(float friction)
        {
            m_friction = friction;
        }

        /// <summary>Get the coefficient of restitution.</summary>
        public float GetRestitution()
        {
            return m_restitution;
        }

        /// <summary>
        /// Set the coefficient of restitution. This will immediately update the mixed restitution
        /// on all associated contacts.
        /// </summary>
        public void SetRestitution(float restitution)
        {
            m_restitution = restitution;
        }

        public AABB GetAABB(int childIndex)
        {
            System.Diagnostics.Debug.Assert(0 <= childIndex && childIndex < m_proxyCount);
            return m_proxies[childIndex].aabb;
        }

        internal Fixture()
        {
            m_userData = null;
            m_body = null;
            m_next = null;
            m_proxies = null;
            m_proxyCount = 0;
            m_shape = null;
            m_density = 0.0f;
        }

        /// <summary>We need separation create/destroy functions from the constructor/destructor because
	    /// the destructor cannot access the allocator or broad-phase (no destructor arguments allowed by C++).</summary>
	    // protected void Create(BroadPhase broadPhase, Body body, Transform xf, FixtureDef def);
        internal void Create(Body body, FixtureDef def)
        {
            m_userData = def.userData; 
            m_friction = def.friction; 
            m_restitution = def.restitution; 
            
            m_body = body; 
            m_next = null; 
            
            m_filter = def.filter; 
            
            m_isSensor = def.isSensor; 
            
            m_shape = def.shape.Clone(); 
            
            // Reserve proxy space
            int childcount = m_shape.GetChildCount();
            m_proxies = new FixtureProxy[childcount];
            for (int i = 0; i < childcount; ++i)
            {
                m_proxies[i] = new FixtureProxy();
                m_proxies[i].fixture = null;
                m_proxies[i].proxyId = BroadPhase.e_nullProxy;
            }
            m_proxyCount = 0;

            m_density = def.density;
        }

        internal void Destroy()
        {
            // The proxy must be destroyed before calling this.
            System.Diagnostics.Debug.Assert(m_proxyCount == 0);

            // Free the proxy array.
            m_proxies = null;

            // Free the child shape.
            m_shape = null;
        }

        internal void CreateProxies(BroadPhase broadPhase, Transform xf)
        {
            System.Diagnostics.Debug.Assert(m_proxyCount == 0);

            // Create proxies in the broad-phase.
            m_proxyCount = m_shape.GetChildCount();

            for (int i = 0; i < m_proxyCount; ++i)
            {
                FixtureProxy proxy = m_proxies[i];
                m_shape.ComputeAABB(out proxy.aabb, ref xf, i);
                proxy.proxyId = broadPhase.CreateProxy(proxy.aabb, proxy);
                proxy.fixture = this;
                proxy.childIndex = i;
                m_proxies[i] = proxy;
            }
        }

        internal void DestroyProxies(BroadPhase broadPhase)
        {
            // Destroy the proxies in the broad-phase.
            for (int i = 0; i < m_proxyCount; ++i)
            {
                FixtureProxy proxy = m_proxies[i];
                broadPhase.DestroyProxy(proxy.proxyId);
                proxy.proxyId = BroadPhase.e_nullProxy;
                m_proxies[i] = proxy;
            }

            m_proxyCount = 0;
        }

        public void Synchronize(BroadPhase broadPhase, ref Transform xf1, ref Transform xf2)
        {
            if (m_proxyCount == 0)
            {
                return;
            }

            for (int i = 0; i < m_proxyCount; ++i)
            {
                FixtureProxy proxy = m_proxies[i];

                // Compute an AABB that covers the swept shape (may miss some rotation effect).
                AABB aabb1, aabb2;
                m_shape.ComputeAABB(out aabb1, ref xf1, proxy.childIndex);
                m_shape.ComputeAABB(out aabb2, ref xf2, proxy.childIndex);

                proxy.aabb.Combine(ref aabb1, ref aabb2);

                Vector2 displacement = xf2.position - xf1.position;

                broadPhase.MoveProxy(proxy.proxyId, ref proxy.aabb, displacement); 

                m_proxies[i] = proxy;
            }
        }

        internal float m_density;

        public Fixture m_next;
        public Body m_body;

        protected Shape m_shape;

        protected float m_friction;
        protected float m_restitution;

        internal FixtureProxy[] m_proxies;
        internal int m_proxyCount;

        protected Filter m_filter;

        internal bool m_isSensor;

        protected Object m_userData;
    }
}
